/*!
* 
* Copyright (c) 2009 by yuqiang @ vcedu
* 
* Description:
*		audiosession, it can send audio and receive audio
*
* ChangeLog:
*		$Date$	$Rev$	$Author$
* 
*/	

#include "stdafx.h"

#include <mipwinmminput.h>
#include <mipwinmmoutput.h>

#include <mipaudiosplitter.h>
#include <mipsampleencoder.h>
#include <mipspeexencoder.h>
#include <miplpcencoder.h>
#include <mipgsmencoder.h>
#include <mipulawencoder.h>
#include <mipalawencoder.h>
#include <miprtpspeexencoder.h>
#include <miprtplpcencoder.h>
#include <miprtpgsmencoder.h>
#include <miprtpalawencoder.h>
#include <miprtpulawencoder.h>
#include <miprtpl16encoder.h>
#include <miprtpcomponent.h>
#include <mipaveragetimer.h>
#include <mipinterchaintimer.h>
#include <miprtpdecoder.h>
#include <miprtpdummydecoder.h>
#include <miprtpspeexdecoder.h>
#include <miprtplpcdecoder.h>
#include <miprtpgsmdecoder.h>
#include <miprtpalawdecoder.h>
#include <miprtpulawdecoder.h>
#include <miprtpl16decoder.h>
#include <mipmediabuffer.h>
#include <mipspeexdecoder.h>
#include <miplpcdecoder.h>
#include <mipgsmdecoder.h>
#include <mipulawdecoder.h>
#include <mipalawdecoder.h>
#include <mipsamplingrateconverter.h>
#include <mipaudiomixer.h>
#include <mipencodedaudiomessage.h>
#include <mipmessagedumper.h>
#include <rtpsession.h>
#include <rtpsessionparams.h>
#include <rtperrors.h>
#include <rtpudpv4transmitter.h>

#include <iostream>
#include <log4cpp/Category.hh>

#include "audiosession.h"

#define AudioSESSION_ERRSTR_NOTINIT						"Not initialized"
#define AudioSESSION_ERRSTR_ALREADYINIT					"Already initialized"
#define AudioSESSION_ERRSTR_MULTIPLIERTOOSMALL			"The input and output sampling interval multipliers must be at least 1"
#define AudioSESSION_ERRSTR_MULTIPLIERTOOLARGE			"The input and output sampling interval multipliers may not exceed 100"
#define AudioSESSION_ERRSTR_NOSPEEX						"Can't use Speex codec since no Speex support was compiled in"
#define AudioSESSION_ERRSTR_NOLPC						"Can't use LPC codec since no LPC support was compiled in"
#define AudioSESSION_ERRSTR_NOGSM						"Can't use GSM codec since no GSM support was compiled in"

static log4cpp::Category& Logger = log4cpp::Category::getInstance(__Module__Name+std::string(".audiosession"));

void AudioSession::onInputThreadExit(bool err, const std::string &compName, const std::string &errStr){
	Logger.info("InputThreadExit : ComponentName[%s] ErrorString[%s]",compName.c_str(),errStr.c_str());
}

void AudioSession::onOutputThreadExit(bool err, const std::string &compName, const std::string &errStr){
	Logger.info("OutputThreadExit : ComponentName[%s] ErrorString[%s]",compName.c_str(),errStr.c_str());
}

AudioSession::AudioSession()
{
	zeroAll();
	m_init = false;
	m_inputRunning = false;
	m_outputRunning = false;
}

AudioSession::~AudioSession()
{
	deleteAll();
}

bool AudioSession::init(const AudioSessionParams *pParams, MIPRTPSynchronizer *pSync, RTPSession *pRTPSession)
{
	if (m_init)
	{
		setErrorString(AudioSESSION_ERRSTR_ALREADYINIT);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_ALREADYINIT));
		return false;
	}

	AudioSessionParams defaultParams;
	const AudioSessionParams *pParams2 = &defaultParams;

	if (pParams){
		pParams2 = pParams;
	}

	if (pParams2->getInputMultiplier() < 1 || pParams2->getOutputMultiplier() < 1)
	{
		setErrorString(AudioSESSION_ERRSTR_MULTIPLIERTOOSMALL);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_MULTIPLIERTOOSMALL));
		return false;
	}

	if (pParams2->getInputMultiplier() > 100 || pParams2->getOutputMultiplier() > 100)
	{
		setErrorString(AudioSESSION_ERRSTR_MULTIPLIERTOOLARGE);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_MULTIPLIERTOOLARGE));
		return false;
	}

	MIPTime inputInterval(0.020 * pParams2->getInputMultiplier());
	MIPTime outputInterval(0.020 * pParams2->getOutputMultiplier());

	int sampRate = 16000;
	int channels = 1;

	if (pParams2->getCompressionType() == Speex)
	{
		if (pParams2->getSpeexEncoding() == NarrowBand)
			sampRate = 8000;
		else if (pParams2->getSpeexEncoding() == WideBand)
			sampRate = 16000;
		else // ultra wide band
			sampRate = 32000;
	}
	else if (pParams2->getCompressionType() == L16Mono){
		sampRate = 44100;
	}
	else{
		sampRate = 8000;
	}

	MIPComponentChain *pActiveChain = 0;
	MIPComponent *pPrevComponent = 0;
	MIPComponent *pInputComponent = 0;

	MIPWinMMInput *pInput = new MIPWinMMInput();
	storeComponent(pInput);

	if (!pInput->open(sampRate, channels, inputInterval, MIPTime(10.0), pParams2->getUseHighPriority()))
	{
		setErrorString(pInput->getErrorString());
		Logger.error(DEBUG_MSG(pInput->getErrorString()));
		deleteAll();
		return false;
	}

	m_pInputChain = new InputChain(this);
	m_pInputChain->setChainStart(pInput);

	pActiveChain = m_pInputChain;
	pPrevComponent = pInput;
	pInputComponent = pInput;


	if (pParams2->getInputMultiplier() > 1)
	{
		MIPAudioSplitter *pSplitter = new MIPAudioSplitter();
		storeComponent(pSplitter);

		if (!pSplitter->init(MIPTime(0.020)))
		{
			setErrorString(pSplitter->getErrorString());
			Logger.error(DEBUG_MSG(pSplitter->getErrorString()));
			deleteAll();
			return false;
		}
		addLink(pActiveChain, &pPrevComponent, pSplitter);
	}

	MIPSampleEncoder *pSampEnc = new MIPSampleEncoder();
	storeComponent(pSampEnc);

	if (pParams2->getCompressionType() == L16Mono)
	{
		if (!pSampEnc->init(MIPRAWAUDIOMESSAGE_TYPE_S16BE))
		{
			setErrorString(pSampEnc->getErrorString());
			Logger.error(DEBUG_MSG(pSampEnc->getErrorString()));
			deleteAll();
			return false;
		}
	}
	else
	{
		if (!pSampEnc->init(MIPRAWAUDIOMESSAGE_TYPE_S16))
		{
			setErrorString(pSampEnc->getErrorString());
			Logger.error(DEBUG_MSG(pSampEnc->getErrorString()));
			deleteAll();
			return false;
		}
	}
	addLink(pActiveChain, &pPrevComponent, pSampEnc);

	switch (pParams2->getCompressionType())
	{
	case Speex:
		{
			MIPSpeexEncoder *pSpeexEnc = new MIPSpeexEncoder();
			storeComponent(pSpeexEnc);

			MIPSpeexEncoder::SpeexBandWidth encoding;

			switch(pParams2->getSpeexEncoding())
			{
			case NarrowBand:
				encoding = MIPSpeexEncoder::NarrowBand;
				break;
			case WideBand:
				encoding = MIPSpeexEncoder::WideBand;
				break;
			case UltraWideBand:
			default:
				encoding = MIPSpeexEncoder::UltraWideBand;
			}

			if (!pSpeexEnc->init(encoding))
			{
				setErrorString(pSpeexEnc->getErrorString());
				deleteAll();
				return false;
			}
			addLink(pActiveChain, &pPrevComponent, pSpeexEnc);

			MIPRTPSpeexEncoder *pRTPEnc = new MIPRTPSpeexEncoder();
			storeComponent(pRTPEnc);

			if (!pRTPEnc->init())
			{
				setErrorString(pRTPEnc->getErrorString());
				Logger.error(DEBUG_MSG(pRTPEnc->getErrorString()));
				deleteAll();
				return false;
			}
			addLink(pActiveChain, &pPrevComponent, pRTPEnc);

			pRTPEnc->setPayloadType(pParams2->getSpeexOutgoingPayloadType());
		}
		break;

	case LPC:
		{
			MIPLPCEncoder *pLPCEnc = new MIPLPCEncoder();
			storeComponent(pLPCEnc);

			if (!pLPCEnc->init())
			{
				setErrorString(pLPCEnc->getErrorString());
				Logger.error(DEBUG_MSG(pLPCEnc->getErrorString()));
				deleteAll();
				return false;
			}
			addLink(pActiveChain, &pPrevComponent, pLPCEnc);

			MIPRTPLPCEncoder *pRTPEnc = new MIPRTPLPCEncoder();
			storeComponent(pRTPEnc);

			if (!pRTPEnc->init())
			{
				setErrorString(pRTPEnc->getErrorString());
				Logger.error(DEBUG_MSG(pRTPEnc->getErrorString()));
				deleteAll();
				return false;
			}
			addLink(pActiveChain, &pPrevComponent, pRTPEnc);

			pRTPEnc->setPayloadType(7);
		}
		break;

	case GSM:
		{
			MIPGSMEncoder *pGSMEnc = new MIPGSMEncoder();
			storeComponent(pGSMEnc);

			if (!pGSMEnc->init())
			{
				setErrorString(pGSMEnc->getErrorString());
				Logger.error(DEBUG_MSG(pGSMEnc->getErrorString()));
				deleteAll();
				return false;
			}
			addLink(pActiveChain, &pPrevComponent, pGSMEnc);

			MIPRTPGSMEncoder *pRTPEnc = new MIPRTPGSMEncoder();
			storeComponent(pRTPEnc);

			if (!pRTPEnc->init())
			{
				setErrorString(pRTPEnc->getErrorString());
				Logger.error(DEBUG_MSG(pRTPEnc->getErrorString()));
				deleteAll();
				return false;
			}
			addLink(pActiveChain, &pPrevComponent, pRTPEnc);

			pRTPEnc->setPayloadType(3);
		}
		break;

	case ALaw:
		{
			MIPALawEncoder *pALawEnc = new MIPALawEncoder();
			storeComponent(pALawEnc);

			if (!pALawEnc->init())
			{
				setErrorString(pALawEnc->getErrorString());
				Logger.error(DEBUG_MSG(pALawEnc->getErrorString()));
				deleteAll();
				return false;
			}
			addLink(pActiveChain, &pPrevComponent, pALawEnc);

			MIPRTPALawEncoder *pRTPEnc = new MIPRTPALawEncoder();
			storeComponent(pRTPEnc);

			if (!pRTPEnc->init())
			{
				setErrorString(pRTPEnc->getErrorString());
				Logger.error(DEBUG_MSG(pRTPEnc->getErrorString()));
				deleteAll();
				return false;
			}
			addLink(pActiveChain, &pPrevComponent, pRTPEnc);

			pRTPEnc->setPayloadType(8);
		}
		break;

	case L16Mono:
		{
			MIPRTPL16Encoder *pRTPEnc = new MIPRTPL16Encoder();
			storeComponent(pRTPEnc);

			if (!pRTPEnc->init(false)) // initialize as mono
			{
				setErrorString(pRTPEnc->getErrorString());
				Logger.error(DEBUG_MSG(pRTPEnc->getErrorString()));
				deleteAll();
				return false;
			}

			addLink(pActiveChain, &pPrevComponent, pRTPEnc);

			pRTPEnc->setPayloadType(11);
		}
		break;

	case ULaw:
	default:
		{
			MIPULawEncoder *pULawEnc = new MIPULawEncoder();
			storeComponent(pULawEnc);

			if (!pULawEnc->init())
			{
				setErrorString(pULawEnc->getErrorString());
				Logger.error(DEBUG_MSG(pULawEnc->getErrorString()));
				deleteAll();
				return false;
			}
			addLink(pActiveChain, &pPrevComponent, pULawEnc);

			MIPRTPULawEncoder *pRTPEnc = new MIPRTPULawEncoder();
			storeComponent(pRTPEnc);

			if (!pRTPEnc->init())
			{
				setErrorString(pRTPEnc->getErrorString());
				Logger.error(DEBUG_MSG(pRTPEnc->getErrorString()));
				deleteAll();
				return false;
			}
			addLink(pActiveChain, &pPrevComponent, pRTPEnc);

			pRTPEnc->setPayloadType(0);
		}
	}

	if (pRTPSession != 0)
	{
		int status;

		m_pRTPSession = pRTPSession;
		m_deleteRTPSession = false;

		if ((status = m_pRTPSession->SetTimestampUnit(1.0/((double)sampRate))) < 0)
		{
			setErrorString(RTPGetErrorString(status));
			Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
			deleteAll();
			return false;		
		}
	}
	else
	{
		RTPUDPv4TransmissionParams transParams;
		RTPSessionParams sessParams;
		int status;

		transParams.SetPortbase(pParams2->getPortbase());
		sessParams.SetOwnTimestampUnit(1.0/((double)sampRate));
		sessParams.SetMaximumPacketSize(2000);
		sessParams.SetAcceptOwnPackets(pParams2->getAcceptOwnPackets());

		m_pRTPSession = new RTPSession();
		m_deleteRTPSession = true;

		if ((status = m_pRTPSession->Create(sessParams,&transParams)) < 0)
		{
			setErrorString(RTPGetErrorString(status));
			Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
			deleteAll();
			return false;
		}
	}

	m_pRTPComp = new MIPRTPComponent();
	storeComponent(m_pRTPComp);

	if (!m_pRTPComp->init(m_pRTPSession))
	{
		setErrorString(m_pRTPComp->getErrorString());
		Logger.error(DEBUG_MSG(m_pRTPComp->getErrorString()));
		deleteAll();
		return false;
	}
	addLink(pActiveChain, &pPrevComponent, m_pRTPComp);

	// create output chain

	m_pOutputChain = new OutputChain(this);

	MIPAverageTimer *pTimer = new MIPAverageTimer(outputInterval);
	storeComponent(pTimer);

	m_pOutputChain->setChainStart(pTimer);

	pActiveChain = m_pOutputChain;
	pPrevComponent = pTimer;
	addLink(pActiveChain, &pPrevComponent, m_pRTPComp);


	MIPRTPDecoder *pRTPDec = new MIPRTPDecoder();
	storeComponent(pRTPDec);

	if (!pRTPDec->init(true, pSync, m_pRTPSession))
	{
		setErrorString(pRTPDec->getErrorString());
		Logger.error(DEBUG_MSG(pRTPDec->getErrorString()));
		deleteAll();
		return false;
	}
	addLink(pActiveChain, &pPrevComponent, pRTPDec);

	MIPRTPDummyDecoder *pRTPDummyDec = new MIPRTPDummyDecoder();
	storePacketDecoder(pRTPDummyDec);

	if (!pRTPDec->setDefaultPacketDecoder(pRTPDummyDec))
	{
		setErrorString(pRTPDec->getErrorString());
		Logger.error(DEBUG_MSG(pRTPDec->getErrorString()));
		deleteAll();
		return false;
	}	

	MIPRTPSpeexDecoder *pRTPSpeexDec = new MIPRTPSpeexDecoder();
	storePacketDecoder(pRTPSpeexDec);

	if (!pRTPDec->setPacketDecoder(pParams2->getSpeexIncomingPayloadType(), pRTPSpeexDec))
	{
		setErrorString(pRTPDec->getErrorString());
		Logger.error(DEBUG_MSG(pRTPDec->getErrorString()));
		deleteAll();
		return false;
	}

	MIPRTPLPCDecoder *pRTPLPCDec = new MIPRTPLPCDecoder();
	storePacketDecoder(pRTPLPCDec);

	if (!pRTPDec->setPacketDecoder(7, pRTPLPCDec))
	{
		setErrorString(pRTPDec->getErrorString());
		Logger.error(DEBUG_MSG(pRTPDec->getErrorString()));
		deleteAll();
		return false;
	}

	MIPRTPGSMDecoder *pRTPGSMDec = new MIPRTPGSMDecoder();
	storePacketDecoder(pRTPGSMDec);

	if (!pRTPDec->setPacketDecoder(3, pRTPGSMDec))
	{
		setErrorString(pRTPDec->getErrorString());
		Logger.error(DEBUG_MSG(pRTPDec->getErrorString()));
		deleteAll();
		return false;
	}

	MIPRTPALawDecoder *pRTPALawDec = new MIPRTPALawDecoder();
	storePacketDecoder(pRTPALawDec);

	if (!pRTPDec->setPacketDecoder(8, pRTPALawDec))
	{
		setErrorString(pRTPDec->getErrorString());
		Logger.error(DEBUG_MSG(pRTPDec->getErrorString()));
		deleteAll();
		return false;
	}

	MIPRTPULawDecoder *pRTPULawDec = new MIPRTPULawDecoder();
	storePacketDecoder(pRTPULawDec);

	if (!pRTPDec->setPacketDecoder(0, pRTPULawDec))
	{
		setErrorString(pRTPDec->getErrorString());
		Logger.error(DEBUG_MSG(pRTPDec->getErrorString()));
		deleteAll();
		return false;
	}

	MIPRTPL16Decoder *pRTPL16Dec = new MIPRTPL16Decoder(false); // for mono sound
	storePacketDecoder(pRTPL16Dec);

	if (!pRTPDec->setPacketDecoder(11, pRTPL16Dec))
	{
		setErrorString(pRTPDec->getErrorString());
		Logger.error(DEBUG_MSG(pRTPDec->getErrorString()));
		deleteAll();
		return false;
	}

	MIPMediaBuffer *pMediaBuf = new MIPMediaBuffer();
	storeComponent(pMediaBuf);

	if (!pMediaBuf->init(outputInterval))
	{
		setErrorString(pMediaBuf->getErrorString());
		Logger.error(DEBUG_MSG(pMediaBuf->getErrorString()));
		deleteAll();
		return false;
	}
	addLink(pActiveChain, &pPrevComponent, pMediaBuf, true);

	MIPSamplingRateConverter *pSampConv = new MIPSamplingRateConverter();
	storeComponent(pSampConv);

	if (!pSampConv->init(sampRate, channels, false))
	{
		setErrorString(pSampConv->getErrorString());
		Logger.error(DEBUG_MSG(pSampConv->getErrorString()));
		deleteAll();
		return false;
	}

	MIPSpeexDecoder *pSpeexDec = new MIPSpeexDecoder();
	storeComponent(pSpeexDec);

	if (!pSpeexDec->init(false))
	{
		setErrorString(pSpeexDec->getErrorString());
		Logger.error(DEBUG_MSG(pSpeexDec->getErrorString()));
		deleteAll();
		return false;
	}
	pActiveChain->addConnection(pMediaBuf, pSpeexDec, false, MIPMESSAGE_TYPE_AUDIO_ENCODED, MIPENCODEDAUDIOMESSAGE_TYPE_SPEEX);
	pActiveChain->addConnection(pSpeexDec, pSampConv, false);

	MIPLPCDecoder *pLPCDec = new MIPLPCDecoder();
	storeComponent(pLPCDec);

	if (!pLPCDec->init())
	{
		setErrorString(pLPCDec->getErrorString());
		Logger.error(DEBUG_MSG(pLPCDec->getErrorString()));
		deleteAll();
		return false;
	}
	pActiveChain->addConnection(pMediaBuf, pLPCDec, false, MIPMESSAGE_TYPE_AUDIO_ENCODED, MIPENCODEDAUDIOMESSAGE_TYPE_LPC);
	pActiveChain->addConnection(pLPCDec, pSampConv, false);

	MIPGSMDecoder *pGSMDec = new MIPGSMDecoder();
	storeComponent(pGSMDec);

	if (!pGSMDec->init())
	{
		setErrorString(pGSMDec->getErrorString());
		Logger.error(DEBUG_MSG(pGSMDec->getErrorString()));
		deleteAll();
		return false;
	}
	pActiveChain->addConnection(pMediaBuf, pGSMDec, false, MIPMESSAGE_TYPE_AUDIO_ENCODED, MIPENCODEDAUDIOMESSAGE_TYPE_GSM);
	pActiveChain->addConnection(pGSMDec, pSampConv, false);

	MIPALawDecoder *pALawDec = new MIPALawDecoder();
	storeComponent(pALawDec);

	if (!pALawDec->init())
	{
		setErrorString(pALawDec->getErrorString());
		Logger.error(DEBUG_MSG(pALawDec->getErrorString()));
		deleteAll();
		return false;
	}
	pActiveChain->addConnection(pMediaBuf, pALawDec, false, MIPMESSAGE_TYPE_AUDIO_ENCODED, MIPENCODEDAUDIOMESSAGE_TYPE_ALAW);
	pActiveChain->addConnection(pALawDec, pSampConv, false);

	MIPULawDecoder *pULawDec = new MIPULawDecoder();
	storeComponent(pULawDec);

	if (!pULawDec->init())
	{
		setErrorString(pULawDec->getErrorString());
		Logger.error(DEBUG_MSG(pULawDec->getErrorString()));
		deleteAll();
		return false;
	}
	pActiveChain->addConnection(pMediaBuf, pULawDec, true, MIPMESSAGE_TYPE_AUDIO_ENCODED, MIPENCODEDAUDIOMESSAGE_TYPE_ULAW);
	pActiveChain->addConnection(pULawDec, pSampConv, true);

	MIPSampleEncoder *pL16SampDec = new MIPSampleEncoder();
	storeComponent(pL16SampDec);

	if (!pL16SampDec->init(MIPRAWAUDIOMESSAGE_TYPE_S16))
	{
		setErrorString(pL16SampDec->getErrorString());
		Logger.error(DEBUG_MSG(pL16SampDec->getErrorString()));
		deleteAll();
		return false;
	}
	pActiveChain->addConnection(pMediaBuf, pL16SampDec, false, MIPMESSAGE_TYPE_AUDIO_RAW, MIPRAWAUDIOMESSAGE_TYPE_S16BE);
	pActiveChain->addConnection(pL16SampDec, pSampConv, false);


	pPrevComponent = pSampConv;

	MIPAudioMixer *pMixer = new MIPAudioMixer();
	storeComponent(pMixer);

	if (!pMixer->init(sampRate, channels, outputInterval, true, false))
	{
		setErrorString(pMixer->getErrorString());
		Logger.error(DEBUG_MSG(pMixer->getErrorString()));
		deleteAll();
		return false;
	}
	addLink(pActiveChain, &pPrevComponent, pMixer, true);

	uint32_t destType;

	destType = MIPRAWAUDIOMESSAGE_TYPE_S16LE;

	MIPWinMMOutput *pOutput = new MIPWinMMOutput();
	storeComponent(pOutput);

	if (!pOutput->open(sampRate, channels, outputInterval, MIPTime(10.0), pParams2->getUseHighPriority()))
	{
		setErrorString(pOutput->getErrorString());
		Logger.error(DEBUG_MSG(pOutput->getErrorString()));
		deleteAll();
		return false;
	}

	MIPSampleEncoder *pSampEnc2 = new MIPSampleEncoder();
	storeComponent(pSampEnc2);

	if (!pSampEnc2->init(destType))
	{
		setErrorString(pSampEnc2->getErrorString());
		Logger.error(DEBUG_MSG(pSampEnc2->getErrorString()));
		deleteAll();
		return false;
	}
	addLink(pActiveChain, &pPrevComponent, pSampEnc2, true);
	addLink(pActiveChain, &pPrevComponent, pOutput, true);

	m_init = true;
	return true;
}

bool AudioSession::destroy()
{
	if (!m_init)
	{
		setErrorString(AudioSESSION_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return false;
	}

	if(m_inputRunning){
		stopSendChain();
	}

	if(m_outputRunning){
		stopReceiveChain();
	}

	deleteAll();
	m_init = false;
	return true;
}

// Note: since we can't be sure that the underlying RTPTransmitter
// was compiled in a thread-safe way, we'll lock the RTP component

bool AudioSession::addDestination(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(AudioSESSION_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return false;
	}

	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->AddDestination(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();

	return true;
}

bool AudioSession::deleteDestination(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(AudioSESSION_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return false;
	}

	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->DeleteDestination(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();

	return true;
}

bool AudioSession::clearDestinations()
{
	if (!m_init)
	{
		setErrorString(AudioSESSION_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return false;
	}

	m_pRTPComp->lock();
	m_pRTPSession->ClearDestinations();
	m_pRTPComp->unlock();
	return true;
}

bool AudioSession::supportsMulticasting()
{
	if (!m_init){
		return false;
	}

	m_pRTPComp->lock();
	bool val = m_pRTPSession->SupportsMulticasting();
	m_pRTPComp->unlock();

	return val;
}

bool AudioSession::joinMulticastGroup(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(AudioSESSION_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return false;
	}

	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->JoinMulticastGroup(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();

	return true;
}

bool AudioSession::leaveMulticastGroup(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(AudioSESSION_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return false;
	}

	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->LeaveMulticastGroup(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();

	return true;
}

bool AudioSession::leaveAllMulticastGroups()
{
	if (!m_init)
	{
		setErrorString(AudioSESSION_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return false;
	}

	m_pRTPComp->lock();
	m_pRTPSession->LeaveAllMulticastGroups();
	m_pRTPComp->unlock();

	return true;
}

bool AudioSession::setReceiveMode(RTPTransmitter::ReceiveMode m)
{
	if (!m_init)
	{
		setErrorString(AudioSESSION_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return false;
	}

	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->SetReceiveMode(m)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();

	return true;
}

bool AudioSession::addToIgnoreList(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(AudioSESSION_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return false;
	}

	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->AddToIgnoreList(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();

	return true;
}

bool AudioSession::deleteFromIgnoreList(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(AudioSESSION_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return false;
	}

	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->DeleteFromIgnoreList(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();

	return true;
}

bool AudioSession::clearIgnoreList()
{
	if (!m_init)
	{
		setErrorString(AudioSESSION_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return false;
	}

	m_pRTPComp->lock();
	m_pRTPSession->ClearIgnoreList();
	m_pRTPComp->unlock();

	return true;
}

bool AudioSession::addToAcceptList(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(AudioSESSION_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return false;
	}

	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->AddToAcceptList(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();

	return true;
}

bool AudioSession::deleteFromAcceptList(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(AudioSESSION_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return false;
	}

	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->DeleteFromAcceptList(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();

	return true;
}

bool AudioSession::clearAcceptList()
{
	if (!m_init)
	{
		setErrorString(AudioSESSION_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return false;
	}

	m_pRTPComp->lock();
	m_pRTPSession->ClearAcceptList();
	m_pRTPComp->unlock();

	return true;
}

void AudioSession::zeroAll()
{
	m_pInputChain = 0;
	m_pOutputChain = 0;
	m_pRTPSession = 0;
	m_components.clear();
	m_packetDecoders.clear();
}

void AudioSession::deleteAll()
{
	// Note: since the output chain may be driven by the input chain, it's a good idea to stop the output chain
	// first.

	if (m_pOutputChain)
	{
		m_pOutputChain->stop();
		delete m_pOutputChain;
	}	

	if (m_pInputChain)
	{
		m_pInputChain->stop();
		delete m_pInputChain;
	}

	std::list<MIPComponent *>::const_iterator it;

	for (it = m_components.begin() ; it != m_components.end() ; it++){
		delete (*it);
	}

	m_components.clear();

	std::list<MIPRTPPacketDecoder *>::const_iterator it2;

	for (it2 = m_packetDecoders.begin() ; it2 != m_packetDecoders.end() ; it2++){
		delete (*it2);
	}

	m_packetDecoders.clear();

	// Note: this should be deleted last, after we can be certain that all RTP
	// packets from this session have been deleted
	if (m_pRTPSession)
	{
		if (m_deleteRTPSession)
		{
			m_pRTPSession->BYEDestroy(RTPTime(2,0),0,0);
			delete m_pRTPSession;
		}
	}

	zeroAll();
}

void AudioSession::storeComponent(MIPComponent *pComp)
{
	bool found = false;
	std::list<MIPComponent *>::const_iterator it;

	for (it = m_components.begin() ; !found && it != m_components.end() ; it++){
		if (pComp == (*it)){
			found = true;
		}
	}

	if (!found){
		m_components.push_back(pComp);
	}
}

void AudioSession::storePacketDecoder(MIPRTPPacketDecoder *pDec)
{
	bool found = false;
	std::list<MIPRTPPacketDecoder *>::const_iterator it;

	for (it = m_packetDecoders.begin() ; !found && it != m_packetDecoders.end() ; it++){
		if (pDec == (*it)){
			found = true;
		}
	}

	if (!found){
		m_packetDecoders.push_back(pDec);
	}
}

void AudioSession::addLink(MIPComponentChain *pChain, MIPComponent **pPrevComp, MIPComponent *pComp, 
							  bool feedback, uint32_t mask1, uint32_t mask2)
{
	pChain->addConnection(*pPrevComp, pComp, feedback, mask1, mask2);
	//	std::cout << "Adding link from " << (*pPrevComp)->getComponentName() << " to " << pComp->getComponentName() << std::endl;
	*pPrevComp = pComp;
}


bool AudioSession::startSendChain(){

	if(NULL == m_pInputChain){
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return FALSE;
	}


	if (!m_pInputChain->start())
	{
		setErrorString(m_pInputChain->getErrorString());
		Logger.error(DEBUG_MSG(m_pInputChain->getErrorString()));
		deleteAll();
		return false;
	}

	m_outputRunning = true;

	return TRUE;
}

bool AudioSession::stopSendChain(){

	if(NULL == m_pInputChain){
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return FALSE;
	}

	if (!m_pInputChain->stop())
	{
		setErrorString(m_pInputChain->getErrorString());
		Logger.error(DEBUG_MSG(m_pInputChain->getErrorString()));
		deleteAll();
		return false;
	}

	m_outputRunning = false;

	return TRUE;
}

bool AudioSession::startReceiveChain(){

	if(NULL == m_pOutputChain){
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return FALSE;
	}

	if (!m_pOutputChain->start())
	{
		setErrorString(m_pOutputChain->getErrorString());
		Logger.error(DEBUG_MSG(m_pOutputChain->getErrorString()));
		deleteAll();
		return false;
	}

	m_inputRunning = true;

	return TRUE;
}

bool AudioSession::stopReceiveChain(){

	if(NULL == m_pOutputChain){
		Logger.error(DEBUG_MSG(AudioSESSION_ERRSTR_NOTINIT));
		return FALSE;
	}

	if (!m_pOutputChain->stop())
	{
		setErrorString(m_pOutputChain->getErrorString());
		Logger.error(DEBUG_MSG(m_pOutputChain->getErrorString()));
		deleteAll();
		return false;
	}

	m_inputRunning = false;

	return TRUE;
}


bool AudioSession::startSession(){
	return (startSendChain() && startReceiveChain());
}

bool AudioSession::stopSession(){
	return (stopSendChain() && stopReceiveChain());
}